package com.ymx.darling.executor.statement;

import com.ymx.darling.bean.entity.id.IdType;
import com.ymx.darling.dialect.keygenerator.JdbcKeyGenerator;
import com.ymx.darling.dialect.keygenerator.KeyGenerator;
import com.ymx.darling.bean.sql.BoundSql;
import com.ymx.darling.bean.sql.Parameter;
import com.ymx.darling.bean.sql.Statement;
import com.ymx.darling.build.Configuration;
import com.ymx.darling.cursor.Cursor;
import com.ymx.darling.cursor.DefaultCursor;
import com.ymx.darling.exception.DarlingException;
import com.ymx.darling.exception.ExceptionFactory;
import com.ymx.darling.executor.mapping.handler.ResultSetHandler;
import com.ymx.darling.type.TypeHandler;
import com.ymx.darling.type.TypeHandlerRegister;
import com.ymx.darling.util.ArrayUtil;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

/**
 * PreparedStatement处理器
 * @author 爱Java的小于
 */
public class PreparedStatementHandler {
    protected final Statement statement;

    public PreparedStatementHandler(Statement statement) {
        this.statement = statement;
    }

    public Statement getStatement() {
        return statement;
    }

    public PreparedStatement preparedStatement(Connection connection)throws SQLException {
        BoundSql boundSql = this.statement.getBoundSql();
        PreparedStatement preparedStatement = this.preparedStatement(connection, boundSql);

        // 绑定参数前生成主键值
        KeyGenerator keyGenerator = this.statement.getKeyGenerator();
        this.processBeforeKeyGenerator(preparedStatement, keyGenerator, boundSql.getParameterObject());

        // 绑定sql参数
        this.bindParameters(boundSql);

        return preparedStatement;
    }


    /**
     * 创建PreparedStatement对象
     * @param connection 数据库连接对象
     * @param boundSql 绑定的sql对象
     * @return PreparedStatement对象
     * @throws SQLException 数据库异常
     */
    protected PreparedStatement preparedStatement(Connection connection, BoundSql boundSql)throws SQLException {
        // 判断设置主键自增返回
        if (this.statement.getKeyGenerator() instanceof JdbcKeyGenerator) {
            return connection.prepareStatement(boundSql.getSql(), java.sql.Statement.RETURN_GENERATED_KEYS);
        }
        return connection.prepareStatement(boundSql.getSql());
    }


    /**
     * 参数绑定前生成主键值并放入实体
     * @param preparedStatement ps
     * @param keyGenerator 主键生成器
     * @param parameterObject 实体类型 批量插入时为 List类型
     */
    protected void processBeforeKeyGenerator(PreparedStatement preparedStatement, KeyGenerator keyGenerator, Object parameterObject)throws SQLException {
        if (keyGenerator.getIdType() == IdType.ASSIGN || keyGenerator.getIdType() == IdType.SEQUENCE) {
            List<Object> entityList = parameterObject instanceof List ? (List<Object>) parameterObject : Collections.singletonList(parameterObject);
            keyGenerator.processBefore(entityList, this.statement);
        }
    }


    /**
     * 插入语句后回填主键值并放入实体
     * @param preparedStatement ps
     * @param keyGenerator 主键生成器
     * @param parameterObject 实体类型 批量插入时为 List类型
     */
    protected void processAfterKeyGenerator(PreparedStatement preparedStatement, KeyGenerator keyGenerator, Object parameterObject)throws SQLException {
        List<Object> entityList = parameterObject instanceof List ? (List<Object>) parameterObject : Collections.singletonList(parameterObject);
        keyGenerator.processAfter(entityList, this.statement, preparedStatement);
    }


    public int update(PreparedStatement preparedStatement)throws SQLException {
        int executeUpdate = preparedStatement.executeUpdate();
        // 设置主键回填
        KeyGenerator keyGenerator = this.statement.getKeyGenerator();
        BoundSql boundSql = this.getStatement().getBoundSql();
        this.processAfterKeyGenerator(preparedStatement, keyGenerator, boundSql.getParameterObject());

        return executeUpdate;
    }


    public <T> List<T> queryList(PreparedStatement preparedStatement)throws SQLException {
        ResultSet resultSet = preparedStatement.executeQuery();
        return this.handResultSet(resultSet);
    }


    /**
     * 查询一个游标接口
     * @param preparedStatement 预编译sql
     * @param <T> T
     * @return cursor
     */
    public <T> Cursor<T> queryCursor(PreparedStatement preparedStatement) {
        return new DefaultCursor<>(preparedStatement, this.statement);
    }


    /**
     * 释放资源
     * @param closeable closeable
     */
    public void close(AutoCloseable closeable) {
        if (Objects.nonNull(closeable)) {
            try {
                closeable.close();
            } catch (Exception e) {
                throw new DarlingException("资源:" + closeable + "关闭失败");
            }
        }
    }


    /**
     * 绑定参数 将参数绑定到sql中
     * @param boundSql boundSql
     */
    protected void bindParameters(BoundSql boundSql) {
        try {
            boundSql.bindSqlParameter();
        } catch (RuntimeException e) {
            Object parameterObject = boundSql.getParameterObject();
            String beforeBindSql = boundSql.getParameterBeforeBindSql();
            throw ExceptionFactory.wrapSqlHandleException("绑定参数时抛出异常", beforeBindSql, ArrayUtil.asList(parameterObject), e);
        }
    }


    /**
     * 设置参数入参
     * @param preparedStatement preparedStatement
     * @throws SQLException SQLException
     */
    public void setParameters(PreparedStatement preparedStatement, List<Object> params) throws SQLException{
        for (int i = 1; i <= params.size(); i++) {
            Object paramValue = params.get(i - 1);
            TypeHandler typeHandler = null;
            if (paramValue instanceof Parameter) {
                Parameter parameter = (Parameter) paramValue;
                paramValue = parameter.getValue();
                typeHandler = parameter.getTypeHandler();
            }
            // 判断注入的值是否为空
            if (Objects.isNull(paramValue)) {
                preparedStatement.setObject(i, null);
            } else {
                if (Objects.isNull(typeHandler)) {
                    typeHandler = TypeHandlerRegister.getTypeHandler(paramValue.getClass());
                    if (Objects.isNull(typeHandler)) {
                        throw new SQLException("darling不支持参数的值类型为'" + paramValue.getClass() + "'如需请参考类型转换器的使用");
                    }
                }
                typeHandler.setParameter(preparedStatement, paramValue, i);
            }
        }
    }


    /**
     * 处理结果集映射
     * @param resultSet 结果集
     * @param <E> E
     * @return E
     */
    protected <E> List<E> handResultSet(ResultSet resultSet) {
        try {
            Configuration configuration = statement.getConfiguration();
            ResultSetHandler<E> resultSetHandler = (ResultSetHandler<E>) configuration.createResultSetHandler(statement);
            return resultSetHandler.handle(resultSet);
        } catch (Exception exception) {
            throw ExceptionFactory.wrapResultMappingException(statement.getModule(), exception);
        } finally {
            this.close(resultSet);
        }
    }
}
